/*!
    \copyright  Copyright (c) 2020 -2025 Qualcomm Technologies International, Ltd.\n
                All Rights Reserved.\n
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    usb_device
    \brief      USB device framework events
*/

#include "usb_device.h"
#include "usb_device_framework.h"

#include "logging.h"

#include <hydra_macros.h>
#include <stdlib.h>
#include <stream.h>
#include <panic.h>
#include <usb.h>

#include "charger_monitor.h"

/* ANC tuning is still using legacy usb_device_class library */
#include "usb_common.h"

#ifdef INCLUDE_USB_DEVICE

/*! List of tasks registered for notifications from USB device framework */
static task_list_t *usb_device_client_tasks;

/*! \brief Initialisation routine for Wired audio source detect module. */
bool UsbDevice_Init(Task init_task)
{
    UNUSED(init_task);

    StreamConfigure(VM_STREAM_USB_ATTACH_MSG_ENABLED, 1);

    return TRUE;
}


/*! Send USB device framework message to registered clients */
static void UsbDevice_NotifyClients(usb_device_msg_t msg_id)
{
    /* Don't waste time if there are no clients */
    if(TaskList_Size(usb_device_client_tasks) == 0)
        return;

   /* Send message to registered clients */
   TaskList_MessageSendId(usb_device_client_tasks, msg_id);
}

void UsbDevice_ClientRegister(Task client_task)
{
    if (!usb_device_client_tasks)
    {
        usb_device_client_tasks = TaskList_Create();
    }

    TaskList_AddTask(usb_device_client_tasks, client_task);
}

void UsbDevice_ClientUnregister(Task client_task)
{
    TaskList_RemoveTask(usb_device_client_tasks, client_task);
}

bool UsbDevice_IsConnectedToHost(void)
{
    usb_attached_status usb_status = Charger_AttachedStatus();

    if((usb_status == HOST_OR_HUB) || (usb_status == HOST_OR_HUB_CHARGER))
    {
        return TRUE;
    }

    return FALSE;
}
#endif /* INCLUDE_USB_DEVICE */

#ifdef MESSAGE_USB_EXT_ALT_INTERFACE

/*! Context data for deferred UsbDevice_CompleteAltInterfaceRequest() call */
typedef struct usb_deferred_response_data
{
    uint16 interface;
    uint16 altsetting;
    uint16 response_context_data;

    struct usb_deferred_response_data *next;
} usb_deferred_response_data_t;

usb_deferred_response_data_t *usb_deferred_queue;

static void usbDevice_DeferRequest(uint16 interface, uint16 altsetting,
        uint16 response_context_data)
{
    usb_deferred_response_data_t *data = (usb_deferred_response_data_t *)
            PanicUnlessMalloc(sizeof(usb_deferred_response_data_t));
    data->interface = interface;
    data->altsetting = altsetting;
    data->response_context_data = response_context_data;

    data->next = NULL;

    /* append to the queue */
    usb_deferred_response_data_t **pp = &usb_deferred_queue;
    while (*pp)
    {
        if ((*pp)->interface == interface)
        {
            DEBUG_LOG_ALWAYS("MESSAGE_USB_EXT_ALT_INTERFACE: duplicate entry interface %d alt %d->%d data 0x%x->0x%x",
                    interface, (*pp)->altsetting, altsetting,
                    (*pp)->response_context_data, response_context_data);
            PanicNull(0);
        }
         pp = &(*pp)->next;
    }
    *pp = data;
}

void UsbDevice_CompleteAltInterfaceRequest(uint16 interface)
{

    usb_deferred_response_data_t **pp = &usb_deferred_queue;
    usb_deferred_response_data_t *data = NULL;

    while (*pp)
    {
        if ((*pp)->interface == interface)
        {
            data = *pp;
            *pp = (*pp)->next;
            break;
        }
        pp = &(*pp)->next;
    }

    if (data)
    {
        DEBUG_LOG_ALWAYS("MESSAGE_USB_EXT_ALT_INTERFACE: intf %d alt %d complete",
                data->interface, data->altsetting);

        /* This call completes processing of MESSAGE_USB_EXT_ALT_INTERFACE */
        UsbAltInterfaceReady(data->interface, data->altsetting,
                data->response_context_data /* control_request_id */);
        free(data);
    }

}

#endif /* MESSAGE_USB_EXT_ALT_INTERFACE */

void UsbDevice_HandleMessage(MessageId id, Message message)
{
    usb_device_t *device = configured_device;
    DEBUG_LOG_ALWAYS("UsbDevice_HandleMessage: 0x%x attached 0x%x configured 0x%x",
            id, attached_device, configured_device);

#ifndef INCLUDE_USB_DEVICE

    if (device == NULL)
    {
        /* Pass into the legacy code in usb_common.c - it is still used
         * by the ANC tuning. */
        Usb_HandleMessage(NULL /* ignored */, id, message);
    }

#else /* INCLUDE_USB_DEVICE */

    bool handled = FALSE;

    switch(id)
    {
    case MESSAGE_USB_ALT_INTERFACE:
    {
        const MessageUsbAltInterface *msg_alt = (const MessageUsbAltInterface *)message;

        if (device)
        {
            for (uint8 i=0; i < device->num_classes; i++)
            {
                usb_device_class_data_t *data = &device->classes[i];
                usb_interface_result_t result = USB_INTERFACE_READY;

                // set interface
                if (data->class->cb->SetInterface)
                {

                    result = data->class->cb->SetInterface(data->context,
                                                           msg_alt->interface,
                                                           msg_alt->altsetting);
                    /* if paniced here, likely interface not created as "external" */
                    PanicFalse(result != USB_INTERFACE_PENDING);
                }
            }
        }

        handled = TRUE;
        break;
    }

#ifdef MESSAGE_USB_EXT_ALT_INTERFACE

    case MESSAGE_USB_EXT_ALT_INTERFACE:
    {
        const MessageUsbExtAltInterface *msg_alt = (const MessageUsbExtAltInterface *)message;

        /* Whether UsbAltInterfaceReady() can be called immediately.
         * Assume "YES" unless one of the classes requested a deferral by
         * returning USB_INTERFACE_PENDING. */
        bool response_ready = TRUE;

        if (device)
        {
            for (uint8 i=0; i < device->num_classes; i++)
            {
                usb_device_class_data_t *data = &device->classes[i];
                usb_interface_result_t result = USB_INTERFACE_NOT_HANDLED;

                if (data->class->cb->SetInterface)
                {
                    /* sanity check */
                    PanicFalse(MESSAGE_USB_EXT_ALT_CLEAR == USB_ALT_SETTING_NONE);

                    result = data->class->cb->SetInterface(data->context,
                                                     msg_alt->interface,
                                                     msg_alt->altsetting);
                }

                if (result != USB_INTERFACE_NOT_HANDLED)
                {
                    /* class can return "USB_INTERFACE_PENDING" when interface
                     * is not immediately ready to be set/cleared. */
                    response_ready = (result != USB_INTERFACE_PENDING);
                    break;
                }
            }
        }

        DEBUG_LOG_ALWAYS("MESSAGE_USB_EXT_ALT_INTERFACE: intf %d alt %d ready %d",
                msg_alt->interface, msg_alt->altsetting, response_ready);

        /* Message must be acknowledged to the platform firmware with
         * UsbAltInterfaceReady() once all device classes are ready to
         * proceed. */
        if (response_ready)
        {
            UsbAltInterfaceReady(msg_alt->interface, msg_alt->altsetting,
                msg_alt->control_request_id);
        }
        else
        {
            usbDevice_DeferRequest(msg_alt->interface, msg_alt->altsetting,
                                   msg_alt->control_request_id);
        }

        handled = TRUE;
        break;
    }

#endif /* MESSAGE_USB_EXT_ALT_INTERFACE */

    case MESSAGE_USB_ENUMERATED:
        UsbDevice_NotifyClients(USB_DEVICE_ENUMERATED);
        break;
    case MESSAGE_USB_DETACHED:
    case MESSAGE_USB_DECONFIGURED:
        UsbDevice_NotifyClients(USB_DEVICE_DECONFIGURED);
        break;

    case MESSAGE_USB_SUSPENDED:
        if (((MessageUsbSuspended *)message)->has_suspended)
        {
            UsbDevice_NotifyClients(USB_DEVICE_SUSPEND);
        }
        else
        {
            UsbDevice_NotifyClients(USB_DEVICE_RESUME);
        }

        break;

    case MESSAGE_USB_ATTACHED:
        break;
    }

    if (device && !handled)
    {
        for (uint8 i = 0; i < device->num_event_handlers; i++)
        {
            device->event_handlers[i](device->index, id, message);
        }
    }
#endif /* INCLUDE_USB_DEVICE */
}


